package com.shell.guard.security;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.shell.bee.base.exception.UnCheckMsgException;
import com.shell.bee.base.utils.Encryptor;
import com.shell.bee.base.utils.StringUtil;
import com.shell.bee.cache.Cache;
import com.shell.bee.cache.CacheBuilder;
import com.shell.bee.entity.auth.AuthUser;
import com.shell.feign.AuthFeign;
import com.shell.feign.PubOrganFeign;
import com.shell.governor.organization.vo.PubOrganVo;
import com.shell.guard.exception.ErrorCodes;


@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

	private static Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
	
	@Autowired
	private AuthFeign authFeign;

	@Autowired
	private PubOrganFeign organFeign;

	private static Cache<String, String> CACHE;

	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		String username = authentication.getName();
		Map<String, Object> data;
		if ((authentication.getDetails() instanceof Map)) {
			data = (Map<String, Object>) authentication.getDetails();
		} else {
			return null;
		}
		logger.debug(data.toString());
		
		String clientId = (String) data.get("client");
		Assert.hasText(clientId, "clientId must have value");
		String entId = (String) data.get("entId");
		String msgVerifyCode = (String) data.get("msgVerifyCode");
		String isMsgLogin = (String) data.get("isMsgLogin");
		String isQrcodeLogin = (String) data.get("isQrcodeLogin");
		String terminalCode = (String) data.get("terminalCode");

		String password = (String) authentication.getCredentials();
		AuthUser authUser = null;
		authUser = this.authFeign.getUserByPrincipal(username);
		if (checkUserState(authUser, username)) {
			if ("true".equals(isMsgLogin)) {
				checkPhoneAndMsgVerifyCode(authUser, username, msgVerifyCode);
			} else if ("true".equals(isQrcodeLogin)) {
				checkUserstate(authUser);
			} else {
				checkUsernameAndPassword(authUser, username, password);
			}
		}
		if (StringUtil.empty(entId)) {
			if ((authUser.getEnt() != null) && (StringUtil.notEmpty(authUser.getEnt().getOrganId()))) {
				entId = authUser.getEnt().getOrganId();
			} else {
				List<PubOrganVo> organs = this.organFeign.queryUserEnt(authUser.getUserId());
				if ((organs != null) && (organs.size() > 0)) {
					entId = ((PubOrganVo) organs.get(0)).getOrganId();
				}
			}
		}
		CustomUserDetails customUserDetails = buildCustomUserDetails(username, password, authUser.getUserId(), clientId,
				entId, terminalCode);
		return new CustomAuthenticationToken(customUserDetails);
	}

	private CustomUserDetails buildCustomUserDetails(String username, String password, String userId, String clientId,
			String ent, String terminalCode) {
		CustomUserDetails customUserDetails = new CustomUserDetails.CustomUserDetailsBuilder().withUserId(userId)
				.withPassword(password).withUsername(username).withClientId(clientId).withEnt(ent)
				.withTerminalCode(terminalCode).build();
		return customUserDetails;
	}

	private boolean checkUserState(AuthUser authUser, String username) {
		if (authUser != null) {
			if ("U".equals(authUser.getUserState())) {
				throw new UnCheckMsgException("该用户已被禁用，请联系管理员");
			}
			if ("F".equals(authUser.getUserState())) {
				throw new UnCheckMsgException("该用户已被冻结，请联系管理员");
			}
		}
		return true;
	}

	private void checkUsernameAndPassword(AuthUser authUser, String username, String password) {
		String passwordMd5 = Encryptor.encryptWithMD5(password);
		if (authUser != null) {
			String realPassword = authUser.getPassword();
			if (!passwordMd5.equals(realPassword)) {
				throw new UnCheckMsgException(ErrorCodes.INVALID_USERNAME_OR_PASSWORD.getDetailMessage());
			}
			if ("F".equals(authUser.getUserState())) {
				throw new UnCheckMsgException(ErrorCodes.USER_FROZEN.getDetailMessage());
			}
		} else {
			throw new UnCheckMsgException(ErrorCodes.INVALID_USERNAME_OR_PASSWORD.getDetailMessage());
		}
	}

	private void checkUserstate(AuthUser authUser) {
		if (authUser != null) {
			if ("F".equals(authUser.getUserState())) {
				throw new UnCheckMsgException(ErrorCodes.USER_FROZEN.getDetailMessage());
			}
		} else {
			throw new UnCheckMsgException(ErrorCodes.INVALID_USERNAME_OR_PASSWORD.getDetailMessage());
		}
	}

	private void checkPhoneAndMsgVerifyCode(AuthUser authUser, String phone, String msgVerifyCode) {
		if (CACHE == null) {
			CACHE = CacheBuilder.newBuilder().build();
		}
		if (CACHE.exists(phone)) {
			String smsCode = (String) CACHE.get(phone);
			if (!smsCode.equals(msgVerifyCode)) {
				throw new UnCheckMsgException(ErrorCodes.INVALID_MSG_VERIFY_CODE.getDetailMessage());
			}
		} else {
			throw new UnCheckMsgException(ErrorCodes.INVALID_MSG_VERIFY_CODE.getDetailMessage());
		}
	}

	public boolean supports(Class<?> aClass) {
		return true;
	}
}
